#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <jsoncpp/json/json.h>

// 这里的协议是用于服务网络版计算器的
namespace ns_protocol
{

// t控制条件编译：自定义方案 && 现成方案
// #define MYSELF 1

// 处理分隔符：这里使用的是空格，定义成宏方便根据需求修改
#define SPACE " "
#define SPACE_LINE strlen(SPACE)
// 加入数据长度，并使用特殊字符（\r\n）区分各段
#define SEP "\r\n"
#define SEP_LINE strlen(SEP)

    /////// 请求：结构体对象 ///////
    class Request
    {
    public:
        // 构造
        Request(){};
        Request(int x, int y, char op)
            : x_(x), y_(y), op_(op)
        {
        }

        // 对请求进行序列化（结构化数据→字节流数据）
        std::string Serialize() // 将x_、y_、op_
        {
#ifdef MYSELF
            // version1： "x_[空格] op_[空格] y_"
            std::string str;
            str = std::to_string(x_); // 先将对应的运算数转换为字符类型：例如32-->"32"。这里注意与ASCII中值为32的字符区别
            str += SPACE;             // 中间以我们设置的间隔符分割（为了反序列化时能够提取每部分）
            str += op_;               // op_本身就是char类型
            str += SPACE;
            str += std::to_string(y_);
            return str;
#else
            // 序列化
            Json::Value root; // 定义一个万能对象，将需要的键值对存入
            root["x"] = x_;
            root["y"] = y_;
            root["op"] = op_;
            Json::FastWriter writer; // 进行序列化并将结果返回
            return writer.write(root);
#endif
        }

        // 对请求进行反序列化（字节流数据→结构化数据）
        bool Deserialized(const std::string &str) // 获取x_、y_、op_
        {
#ifdef MYSELF
            //----------------------------------
            // version1： "x_[空格] op_[空格] y_" 根据分隔符提取有效数放入结构化对象中
            // 例如："1234[空格]+[空格]5678"
            // a、找左运算数
            std::size_t left_oper = str.find(SPACE);
            if (left_oper == std::string::npos) // 没找到
                return false;
            // b、找右运算数
            std::size_t right_oper = str.rfind(SPACE);
            if (right_oper == std::string::npos) // 没找到
                return false;
            // c、提取运算数，赋值给结构化对象成员
            x_ = atoi((str.substr(0, left_oper)).c_str());            // string substr (size_t pos = 0, size_t len = npos) const;
            y_ = atoi((str.substr(right_oper + SPACE_LINE).c_str())); // 注意这里右运算符需要将[空格]跳过
            if (left_oper + SPACE_LINE > str.size())
                return false;
            else
                op_ = str[left_oper + SPACE_LINE]; // 提取运算符时也要注意跳过分隔符[空格]
            return true;
            //----------------------------------
#else
            // 反序列化
            Json::Value root;
            Json::Reader reader;
            reader.parse(str, root); // 对str进行反序列化，将结果取出
            x_ = root["x"].asInt();
            y_ = root["y"].asInt();
            op_ = root["op"].asInt();
            return true;
#endif
        }

    public:
        int x_;   // 左运算数
        int y_;   // 右运算数
        char op_; // 运算符
    };

    /////// 响应：结构体对象 ///////
    class Response
    {
    public:
        // 构造函数
        Response(int result, int code)
            : result_(result), code_(code)
        {
        }
        Response() {}

        // 析构函数
        ~Response() {}

        // 对响应序列化（结构化数据→字节流数据）
        std::string Serialize()
        {
#ifdef MYSELF
            // version1："code_ [空格] result_"
            // 例如："0[空格]6912"
            std::string str;
            str = std::to_string(code_);
            str += SPACE;
            str += std::to_string(result_);
            return str;
#else
            // 序列化
            Json::Value root;
            root["code"] = code_;
            root["result"] = result_;
            Json::FastWriter writer;
            return writer.write(root); // 将序列化后的结果返回
#endif
        }

        // 对响应反序列化（字节流数据→结构化数据）
        bool Deserialized(const std::string &str)
        {
#ifdef MYSELF
            //----------------------------------
            // version1："code_ [空格] result_"
            // 例如："0[空格]6912"
            // a、找分隔符
            std::size_t pos = str.find(SPACE);
            if (pos == std::string::npos) // 没找到
                return false;
            // b、获取状态码
            code_ = atoi((str.substr(0, pos)).c_str());
            // c、获取计算结果
            result_ = atoi((str.substr(pos + SPACE_LINE)).c_str());
            return true;
            //----------------------------------
#else
            // 反序列化
            Json::Value root;
            Json::Reader reader;
            reader.parse(str, root);
            code_ = root["code"].asInt();
            result_ = root["result"].asInt();
            return true;
#endif
        }

    public:
        int result_; // 计算结果
        int code_;   // 状态码：用于判断结果是否正常
    };

    // 从网络中读取
    bool Recv(int sock, std::string &out_buffer)
    {
        char buffer[1024];
        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0); // 阻塞式从网络中读取字节流数据
        if (s > 0)                                             // 读取成功
        {
            buffer[s] = 0;        // 文件尾字符\0
            out_buffer += buffer; // 使用+=，输出到out_buffer中，这样在多次读取的情况下能保证数据连续性
        }
        else if (s == 0)
        {
            std::cout << "recv:quit." << std::endl;
            return false;
        }
        else
        {
            std::cout << "recv error." << std::endl;
            return false;
        }
        return true;
    }

    // 向网络中发送
    void Send(int sock, const std::string &str)
    {
        ssize_t s = send(sock, str.c_str(), str.size(), 0);
        if (s < 0)
        {
            std::cout << "send error" << std::endl;
        }
    }

    // length\r\nx_[空格]op_[空格]y_\r\n
    // 实际正文部分：x_[空格]op_[空格]y_，后续需要对正文部分序列化
    std::string Decode(std::string &buffer)
    {
        // 找有没有\r\n
        std::size_t pos = buffer.find(SEP);
        if (pos == std::string::npos) // 没找到，说明本次读取/接收到的报文不完整，需要继续读取/接收
            return "";
        // 执行到此，说明确实有\r\n，但不代表数据完整。此时需要提取length值，与实际正文做比较，判断是否读取到一个完整的报文
        int length_size = atoi(buffer.substr(0, pos).c_str()); // 获取长度信息
        int remain_size = buffer.size() - pos - 2 * SEP_LINE;  // 获取剩余长度
        if (remain_size >= length_size)                        // 说明此时缓冲区buffer中存在一个完整的报文，可以提取。
        {
            // 举例：【length\r\nXXXXXXXXX\r\nlength\r\nXXXXXX\r\n】
            // string& erase (size_t pos = 0, size_t len = npos);
            buffer.erase(0, pos + SEP_LINE);                 // 移除缓冲区中的length\r\n，即【XXXXXXXXX\r\nlength\r\nXXXXXX\r\n 】
            std::string str = buffer.substr(0, length_size); // 获取length长度的字串：即【XXXXXXXXX】
            buffer.erase(0, length_size + SEP_LINE);         // 移除缓冲区中正文及尾随的\r\n，即【length\r\nXXXXXX\r\n 】
            return str;
        }
        else
            return ""; // 说明本次读写缓冲区中报文不完整，需要继续读写。
    }

    // 返回一个带有长度信息的完整报头：实际正文部分为 x_[空格]op_[空格]y_，需要为其添加长度信息,变为length\r\nx_[空格]op_[空格]y_\r\n
    // 例如：123 * 456 --->9\r\n123 * 456\r\n
    std::string Encode(std::string &s)
    {
        // 1、获取正文长度
        std::string package = std::to_string(s.size());
        // 2、加上SEP分隔符
        package += SEP;
        // 3、加上正文
        package += s;
        // 4、加上SEP分隔符
        package += SEP;
        return package;
    }

}